P04-1. Zero-Trust 보안과 SPIFFE SVID 인증 인가 - 이론 개념
Part 04 (이론): Zero-Trust 보안과 SPIFFE/SVID 인증 · 인가
- 전통적인 성벽형 보안(경계 보안)이 쿠버네티스 환경에서 왜 무력화되는지(측면 이동 공격과 파드 IP의 휘발성), 그리고 Zero-Trust(제로 트러스트) 가 이를 어떻게 해결하는지 설명할 수 있다.
- IP 주소 대신 파드의 신분을 증명하는 CNCF 표준 규격인
SPIFFEID(spiffe://cluster.local/ns/.../sa/...) 와SVID(X.509 인증서 SAN 확장 필드) 의 구조를 해부한다. - 인증(
PeerAuthentication:PERMISSIVEvsSTRICT) 과 인가(AuthorizationPolicy:DENYvsALLOW우선순위) 의 차이를 명확히 구분한다. - Ambient Mesh에서 L4 인가 정책은 왜
ztunnel이 집행하고, L7 인가 정책(GET/DELETE, URL 경로)은 왜 반드시Waypoint Proxy가 집행해야 하는지 아키텍처 수준에서 이해한다.
1. 왜 쿠버네티스에서는 IP 방화벽이 소용없을까? (Zero-Trust의 탄생)
과거 온프레미스 서버나 전통적인 VM 환경에서는 "성벽과 해자(Castle-and-Moat)" 모델, 즉 경계 기반 보안(Perimeter Security) 을 사용했습니다.
회사 데이터센터 정문에 비싼 방화벽 하나를 튼튼하게 세워 두고, 일단 그 정문을 통과해 내부망(사설 IP 대역)에 들어온 서버들끼리는 서로를 100% 믿고 평문(HTTP)으로 자유롭게 통신하게 두었습니다. 또한 방화벽 규칙도 "웹 서버(192.168.1.10)에서 DB 서버(192.168.2.50)의 5432 포트로 가는 것만 허용해라" 처럼 고정된 IP 주소를 기준으로 짰습니다.
하지만 클라우드 네이티브(쿠버네티스) 환경에서는 이 두 가지 가정이 완전히 깨집니다.
- 측면 이동 공격 (Lateral Movement):
쿠버네티스의 기본 네트워크(CNI)는 클러스터 안의 모든 파드가 서로 통신할 수 있는 평평한 네트워크(Flat Network)입니다. 만약 해커가 외부에 노출된 덜 중요한 이벤트 페이지 파드 하나를 해킹해 들어오면, 내부망에서는 아무런 신분 검사를 하지 않기 때문에 그 파드를 발판 삼아 옆에 있는 결제 API 파드나 고객 DB까지 아무 제지 없이 옮겨 다닐 수 있습니다. - 파드 IP의 휘발성 (Ephemeral Pod IP):
쿠버네티스에서 파드는 오토스케일링이나 배포 때마다 하루에도 수십 번씩 죽고 새로 태어나며, 그때마다 IP 주소(10.244.x.x)가 계속 바뀝니다. 심지어 1분 전까지 결제 파드가 쓰던10.244.1.5IP를, 결제 파드가 죽자마자 엉뚱한 프론트엔드 파드가 물려받아 쓸 수도 있습니다. 즉, 쿠버네티스에서 "IP 주소 = 서버의 신분"이라는 공식은 성립하지 않습니다.
이 한계를 극복하기 위해 등장한 현대 보안의 대원칙이 바로 Zero-Trust(제로 트러스트) 입니다.
- 핵심 슬로건: "Never Trust, Always Verify (절대 믿지 말고, 매번 검증하라)"
- 개념: 패킷이 우리 회사 내부망(같은 클러스터, 심지어 같은 워커 노드) 안에서 날아왔다는 이유만으로 절대 신뢰하지 않습니다. 모든 파드 간 요청(동서 트래픽)마다 다음 세 가지를 강제합니다:
- 모든 통신의 암호화 (
mTLS): 내부망 도청 원천 차단 - 암호학적 신원 확인 (
Authentication, 인증): IP가 아니라 위조 불가능한 전자 여권(SPIFFE)으로 "너는 정말 누구냐?" 를 확인 - 최소 권한 부여 (
Authorization, 인가): 신원이 확인되었더라도 "너에게 이 API 경로를 호출할 권한이 있는가?" 를 매 요청마다 심사
- 모든 통신의 암호화 (
2. IP를 대체하는 파드의 전자 여권: SPIFFE 와 SVID
IP 주소를 믿을 수 없다면, ztunnel과 Waypoint는 도대체 무엇을 보고 상대방 파드의 신분을 확인할까요?
바로 쿠버네티스의 ServiceAccount(서비스 어카운트) 와 연동된 국제 표준 신분증 규격인 SPIFFE 입니다!
flowchart LR
subgraph K8S["1. K8s 리소스 정의"]
SA["ServiceAccount: order-sa
Namespace: part1-demo"] --> POD["order-api 파드 실행
(spec.serviceAccountName: order-sa)"]
end
subgraph ISTIOD["2. istiod (CA 인증기관)"]
CA["내장 인증기관 (Citadel)
X.509 전자 인증서(SVID) 서명·발급"]
end
subgraph CERT["3. 발급된 SVID 인증서 내부 (X.509 SAN 필드)"]
URI["URI: spiffe://cluster.local/ns/part1-demo/sa/order-sa
유효기간: 24시간 (자동 갱신)"]
end
POD -->|"파드 생성/노드 배치"| CA -->|"SDS 보안 채널로 ztunnel에 전달"| CERTServiceAccount, SPIFFE, SVID, X.509 SAN
ServiceAccount(서비스 어카운트):- 쿠버네티스에서 '사람(개발자)'이 쓰는 계정이 아니라, '파드(프로그램)'에게 부여하는 전용 계정(명찰) 입니다. 파드를 만들 때 따로 지정하지 않으면 그 네임스페이스의 기본 계정인
defaultServiceAccount가 자동으로 붙지만, 실무 보안에서는 마이크로서비스마다 전용 계정(order-sa,payment-sa등)을 만들어 붙여 줍니다.
- 쿠버네티스에서 '사람(개발자)'이 쓰는 계정이 아니라, '파드(프로그램)'에게 부여하는 전용 계정(명찰) 입니다. 파드를 만들 때 따로 지정하지 않으면 그 네임스페이스의 기본 계정인
SPIFFE(Secure Production Identity Framework for Everyone, 스피피):- 파드나 VM의 IP 주소가 바뀌더라도 변하지 않는 전 세계 표준 워크로드 주민등록번호 체계(CNCF 졸업 프로젝트)입니다.
- 형식은 항상 다음과 같은 URI 구조를 가집니다:
spiffe://<신뢰도메인(Trust Domain)>/ns/<네임스페이스>/sa/<서비스어카운트>
(예:spiffe://cluster.local/ns/part1-demo/sa/order-sa)
SVID(SPIFFE Verifiable Identity Document):- 위
SPIFFE주민등록번호를istiod본부(인증기관, CA)가 위조 불가능한 X.509 디지털 인증서 안에 도장을 찍어 발급해 준 실물 전자 여권입니다.
- 위
X.509 SAN(Subject Alternative Name, 주체 대체 이름):- 디지털 인증서 안에서 소유자의 신분(
spiffe://...URI나 도메인 이름)을 적어 넣는 표준 확장 칸입니다.ztunnel끼리HBONE(:15008)터널을 맺을 때 서로의 인증서 속 SAN 칸에 적힌spiffe://...문자열을 꺼내어 상대방이 누구인지 100% 암호학적으로 검증합니다.
- 디지털 인증서 안에서 소유자의 신분(
3. 1단계 관문 — 인증 (Authentication: 너는 누구냐?)
보안 검문은 항상 1단계 인증(Authentication, 줄여서 AuthN) 과 2단계 인가(Authorization, 줄여서 AuthZ) 순서로 진행됩니다.
공항 출입국 심사대에 비유하면 이해가 아주 쉽습니다.
- 인증 (
Authentication— 여권 진위 심사): "당신이 제시한 여권(SVID)이 대한민국 외교부(istiod CA)가 발급한 진짜 여권이 맞습니까? 아니면 위조여권이거나 여권이 아예 없는 밀입국자(평문 HTTP)입니까?" - 인가 (
Authorization— 비자 및 출입 구역 심사): "여권이 진짜(study-client)인 건 확인했습니다. 그런데 당신에게 일급 보안 구역(DELETE /admin)에 들어갈 출입 권한이 있습니까?"
Istio에서 1단계인 파드 간 mTLS 인증 강도를 조절하는 쿠버네티스 리소스가 바로 PeerAuthentication 입니다.
PeerAuthentication 모드 |
동작 방식 (공항 비유) | 언제 사용하나요? |
|---|---|---|
PERMISSIVE (기본값, 허용 모드) |
상대방이 암호화된 터널(mTLS / HBONE)로 오면 신분증을 검사해 받고, 아직 메시에 들어오지 않은 레거시 파드가 평문(Plaintext TCP)으로 요청해도 일단 문은 열어 줌 |
기존 운영 중인 클러스터에 서비스 메시를 처음 도입하며 파드들을 순차적으로 메시에 편입(마이그레이션)할 때 |
STRICT (엄격 모드, Zero-Trust 표준) |
무조건 유효한 SPIFFE 신분증으로 암호화된 mTLS(HBONE) 통신만 허용! 신분증이 없는 평문 접근은 그 즉시 연결 거부 |
모든 워크로드가 메시에 편입된 후, 내부망 도청과 미인증 파드의 접근을 100% 차단할 때 |
4. 2단계 관문 — 인가 (Authorization: 무엇을 할 자격이 있느냐?)
여권 심사(PeerAuthentication)를 통과해 상대방의 정확한 신분(spiffe://cluster.local/ns/part1-demo/sa/study-client-sa)이 확인되었다면, 이제 AuthorizationPolicy 리소스로 정교한 출입 통제 규칙을 적용합니다.
4-1. AuthorizationPolicy 평가 우선순위 (반드시 알아야 할 황금 규칙!)
한 네임스페이스나 서비스에 여러 개의 AuthorizationPolicy가 걸려 있을 때, Istio는 아래 순서대로 심사합니다.
flowchart LR
REQ["패킷 도착
(신분: spiffe://.../sa/client)"] --> STEP1{"1. DENY(거부) 정책에
하나라도 걸리는가?"}
STEP1 -->|"예 (매칭됨)"| BLOCK1["즉시 차단!
(Deny Wins)"]
STEP1 -->|"아니오"| STEP2{"2. 등록된 ALLOW(허용)
정책이 존재하는가?"}
STEP2 -->|"아예 없음 (0개)"| PASS1["통과 허용
(기본 개방 상태)"]
STEP2 -->|"1개 이상 있음"| STEP3{"3. ALLOW 정책 조건에
정확히 일치하는가?"}
STEP3 -->|"예 (매칭됨)"| PASS2["통과 허용!"]
STEP3 -->|"아니오 (명단에 없음)"| BLOCK2["즉시 차단!
(화이트리스트 모드 작동)"]ALLOW 정책을 단 1개라도 만드는 순간 화이트리스트(Default Deny)가 됩니다!
- 아무런
AuthorizationPolicy가 없을 때는 모든 파드의 통신이 허용됩니다. - 하지만 특정 서비스에
action: ALLOW정책을 단 1개라도 등록하는 순간, 그 정책에 적히지 않은 나머지 모든 접근은 자동으로 전면 차단(Default Deny) 됩니다! 즉, "A에게만 허용한다 = A가 아닌 나머지는 전부 막는다" 는 뜻으로 동작하므로 보안 사고를 막는 데 매우 강력합니다.
4-2. Ambient Mesh의 핵심 설계: L4 인가(ztunnel) vs L7 인가(Waypoint)
Part 03에서 배웠듯 Ambient Mesh는 데이터 플레인을 노드 레벨 L4 프록시(ztunnel) 와 네임스페이스/서비스 레벨 L7 프록시(Waypoint) 로 철저히 분리했습니다.
따라서 우리가 작성하는 AuthorizationPolicy도 안에서 어떤 조건을 검사하느냐에 따라 집행관이 달라집니다!
| 구분 | L4 인가 정책 (ztunnel 단독 집행) |
L7 인가 정책 (Waypoint Proxy 필수!) |
|---|---|---|
| 검사 가능한 조건 | 출발지 신분(principals / ServiceAccount), 출발지 네임스페이스(namespaces), 출발지 IP(ipBlocks), 목적지 포트(ports) |
위 L4 조건 전체 + HTTP 메서드(GET, POST, DELETE), URL 경로(paths: ["/admin*"]), HTTP 헤더(headers), 도메인(hosts) |
| 누가 검문하나요? | 서버 노드의 ztunnel (Rust L4 프록시) |
네임스페이스/서비스의 Waypoint Proxy (Envoy L7 프록시) |
| 차단 시 클라이언트가 보는 현상 | TCP 연결 자체가 즉시 끊김 (curl: (56) Recv failure: Connection reset by peer 또는 빈 응답) |
정식 HTTP 상태 코드 403 Forbidden 과 본문 RBAC: access denied 반환 |
만약 Waypoint 없이 ztunnel만 있는 상태에서 L7 규칙(methods: ["GET"])을 넣으면? |
ztunnel은 보안상 HTTP 본문을 뜯어보지 않으므로 "어? 이 정책에는 내가 읽을 수 없는 L7 조건이 들어있네? 잘못 열었다가 보안 사고가 나느니 아예 전면 차단(Fail-Close)하자!" 라며 트래픽을 막아버립니다! |
따라서 HTTP 메서드나 URL 경로 기반 정책을 쓸 때는 반드시 Waypoint Proxy 를 함께 배포해야 합니다. |
🔗 다음 단계 (실습 검증)
이론으로 배운 Zero-Trust 보안의 모든 요소를 이제 우리의 docker-desktop 클러스터에서 직접 검증해 봅니다!
ztunnel이 메모리에 들고 있는 실제SPIFFEX.509 인증서를 꺼내어SAN필드에 적힌spiffe://cluster.local/...신분증을 눈으로 확인하고,PeerAuthentication(STRICT) 와AuthorizationPolicy를 이용해 허가받은ServiceAccount만 접근을 허용하고,Waypoint Proxy를 통해GET /headers요청은200 OK로 통과시키되DELETE요청이나/admin경로 접근은403 RBAC: access denied로 칼같이 차단하는 실습을 진행합니다.
네비게이션
| 이전 | 목록 | 다음 |
|---|---|---|
| ◀ Part 03 실습 | 목록으로 | Part 04 실습 ▶ |